package profiler;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import profiler.exec.MutableExecution;

public class SingletonTimer {

    private static final ThreadLocalTimerImpl INSTANCE = new ThreadLocalTimerImpl();

    public static Timing getInstance() {
        return INSTANCE;
    }

    private static Map<String, Long> keys = new HashMap<String, Long>();

    private static Map<Long, String> keyNames = new HashMap<Long, String>();
    
    private static long keyGenerator = 0;

    private static final Queue<MutableExecution> executions = new ConcurrentLinkedQueue<MutableExecution>();

    public static Collection<? extends MutableExecution> getExecutions() {
        return executions;
    }

    public static synchronized long getOrCreateKey(String identifier) {
        Long value = keys.get(identifier);
        if (value != null) {
            return value;
        }
        value = keyGenerator++;
        keys.put(identifier, value);
        keyNames.put(value, identifier);
        return value;
    }

    private static class ThreadLocalTimerImpl implements Timing {

        private final ThreadLocal<MutableExecution> callStack = new ThreadLocal<MutableExecution>();

        public void startMethodCall(String identifier) {
            MutableExecution parent = callStack.get();
            MutableExecution child;
            if (parent == null) {
                child = new MutableExecution(getOrCreateKey(identifier));
                executions.add(child);
            } else {
                child = parent.addChild(identifier);
            }
            callStack.set(child);
            child.start();
        }

        public void stop() {
            MutableExecution current = callStack.get();
            current.stop();
            callStack.remove();
            if (!current.isRoot()) {
                callStack.set((MutableExecution) current.parent());
            }
        }
    }

    public synchronized static String getIdentifier(long key) {
        return keyNames.get(key);
    }

}
